package com.yfqy.admin.security.login.username;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.yfqy.admin.constant.MyConstants;
import com.yfqy.admin.domain.dto.auth.AuthUser;
import com.yfqy.admin.enums.LoginTypeEnum;
import com.yfqy.admin.security.login.LoginUserInfo;
import com.yfqy.admin.service.SystemUserService;
import jakarta.annotation.Resource;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 帐号密码登录认证
 */
@Component
public class UsernameAuthenticationProvider implements AuthenticationProvider {

    @Autowired
    private SystemUserService userService;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Resource(name = "singleRedisClient")
    private RedissonClient redissonClient;

    public UsernameAuthenticationProvider() {
        super();
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 用户提交的用户名 + 密码：
        String username = (String) authentication.getPrincipal();
        String password = (String) authentication.getCredentials();

        // 查数据库，匹配用户信息
        AuthUser user = userService.getUserFromDB(username);
        if (user == null
                || !passwordEncoder.matches(password, user.getPassword())) {
            // 密码错误，直接抛异常。
            // 根据SpringSecurity框架的代码逻辑，认证失败时，应该抛这个异常：org.springframework.security.core.AuthenticationException
            // BadCredentialsException就是这个异常的子类
            // 抛出异常后后，AuthenticationFailureHandler的实现类会处理这个异常。
            throw new BadCredentialsException("${invalid.username.or.pwd:用户名或密码不正确}");
        }

        // 转换权限为Spring Security格式
        List<GrantedAuthority> authorities = Optional.ofNullable(user.getAuthorities()).orElse(Collections.emptyList())
                .stream()
                .map(auth -> new SimpleGrantedAuthority(auth.getCode()))
                .collect(Collectors.toList());

        List<Map<String, String>> authorityMapList = authorities.stream()
                .map(grantedAuthority -> {
                    Map<String, String> map = new LinkedHashMap<>();
                    map.put("authority", grantedAuthority.getAuthority());
                    return map;
                })
                .collect(Collectors.toList());
        // redis 缓存用户权限code
        RBucket<Object> userBucket = redissonClient.getBucket(String.format(MyConstants.SYS_USER_TOKEN, user.getId()));
        userBucket.set(JSONUtil.toJsonStr(authorityMapList), 30L, TimeUnit.DAYS);

        LoginUserInfo loginUser = BeanUtil.toBean(user, LoginUserInfo.class);
        loginUser.setAuthorities(authorities); // 设置权限列表
        loginUser.setLoginType(LoginTypeEnum.USERNAME);
        UsernameAuthentication usernameAuthentication = new UsernameAuthentication(loginUser);
        usernameAuthentication.setAuthenticated(true);
        return usernameAuthentication;
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return authentication.isAssignableFrom(UsernameAuthentication.class);
    }
}

